import 'reflect-metadata'
import fs from 'fs'
import path from 'path'
import jwt from 'jsonwebtoken'
const serverConfig =  require('./server.config.json');

const METHOD_METADATA = 'method'
const PATH_METADATA = 'path'

const Controller = (path: string): ClassDecorator => {
  return (target) => {
    Reflect.defineMetadata(PATH_METADATA, path, target)
  }
}

const createMappingDecorator = (method: string) => (path: string) => {
  return (target, key, descriptor) => {
    Reflect.defineMetadata(PATH_METADATA, path, descriptor.value)
    Reflect.defineMetadata(METHOD_METADATA, method, descriptor.value)
  }
}

function mapRoute(instance: Object) {
  const prototype = Object.getPrototypeOf(instance)
  const methodsNames = Object.getOwnPropertyNames(prototype)
  return methodsNames
    .map((methodName) => {
      const fn = prototype[methodName]
      const route = Reflect.getMetadata(PATH_METADATA, fn)
      const method = Reflect.getMetadata(METHOD_METADATA, fn)
      return {
        route,
        method,
        fn,
        methodName,
      }
    })
    .filter((item) => item.method)
}
const Get = createMappingDecorator('get')
const Post = createMappingDecorator('post')
const Put = createMappingDecorator('put')
const Delete = createMappingDecorator('delete')

function generateToken(data, time: number = serverConfig.WEB_API.TOKEN_TIME) {
  let created = Math.floor(Date.now() / 1000)
  let cert = fs.readFileSync(
    path.join(__dirname, './config/rsa_private_key.pem')
  )
  let token = jwt.sign(
    {
      data,
      exp: created + 60 * time,
    },
    cert,
    {algorithm: 'RS256'}
  )
  return token
}

function verifyToken(token){
  let cert = fs.readFileSync(path.join(__dirname, './config/rsa_public_key.pem'))
  let res
  try {
      let result = jwt.verify(token, cert, {algorithm: ['RS256']}) || {}
      let {exp = 0 } = result, current = Math.floor(Date.now() / 1000)
      if(current <= exp){
          res = result.data || {}
      }
  } catch (error) {
      res = 'err'
  }
  return res
}

function Auth(target, key, descriptor){
  const fn = descriptor.value
  if(typeof fn === 'function'){
    descriptor.value = function(...args){
      const req = args[0]
      const res = args[1]
      const next = args[2]
      let result = verifyToken(req.headers.token)
      if(result === 'err'){
        return res.send({status: 403, msg: '登录已过期，请重新登录!'});
      } else {
        next()
      }
      const func = fn.apply(this, args)
      return func
    }
  }
}
export {
  Controller, 
  mapRoute, 
  Get, 
  Post, 
  Put, 
  Delete,
  generateToken,
  Auth
}
